package com.tnovoselec.android.ui.drawable;

import java.util.Arrays;

import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;

/**
 * A Drawable object that draws primitive shapes. A ShapeDrawable takes a
 * {@link android.graphics.drawable.shapes.Shape} object and manages its
 * presence on the screen. If no Shape is given, then the ShapeDrawable will
 * default to a {@link android.graphics.drawable.shapes.RectShape}.
 * 
 * <p>
 * This object can be defined in an XML file with the <code>&lt;shape></code>
 * element.
 * </p>
 * 
 * 
 * @attr ref android.R.styleable#ShapeDrawablePadding_left
 * @attr ref android.R.styleable#ShapeDrawablePadding_top
 * @attr ref android.R.styleable#ShapeDrawablePadding_right
 * @attr ref android.R.styleable#ShapeDrawablePadding_bottom
 * @attr ref android.R.styleable#ShapeDrawable_color
 * @attr ref android.R.styleable#ShapeDrawable_width
 * @attr ref android.R.styleable#ShapeDrawable_height
 */
public class PieDrawable extends Drawable {

    private PieState mPieState;
    private boolean mMutated;

    /**
     * ShapeDrawable constructor.
     */
    public PieDrawable() {
	this((PieState) null);
    }

    /**
     * Creates a ShapeDrawable with a specified Shape.
     * 
     * @param s
     *            the Shape that this ShapeDrawable should be
     */
    public PieDrawable(int[] colors) {
	this(colors, 0);
    }

    public PieDrawable(int[] colors, int rotation) {
	this((PieState) null);

	mPieState.mColors = colors;
	if (colors.length == 2 && rotation == 0) {
	    mPieState.mRotation = 90;
	} else {
	    mPieState.mRotation = rotation;
	}
    }

    private PieDrawable(PieState state) {
	mPieState = new PieState(state);
    }

    /**
     * Returns the Shape of this ShapeDrawable.
     */
    public int[] getColors() {
	return mPieState.mColors;
    }

    public float getRotation() {
	return mPieState.mRotation;
    }

    /**
     * Sets the Shape of this ShapeDrawable.
     */
    public void setColors(int[] colors) {
	mPieState.mColors = colors;
	updateDrawable();
    }

    public void setRotation(float rotation) {
	mPieState.mRotation = rotation;
	updateDrawable();
    }

    /**
     * Returns the Paint used to draw the shape.
     */
    public Paint getFillPaint() {
	return mPieState.mFillPaint;
    }

    public Paint getStrokePaint() {
	return mPieState.mStrokePaint;
    }

    public Paint getDividerPaint() {
	return mPieState.mDividerPaint;
    }

    /**
     * Sets the intrinsic (default) width for this shape.
     * 
     * @param width
     *            the intrinsic width (in pixels)
     */
    public void setIntrinsicWidth(int width) {
	mPieState.mIntrinsicWidth = width;
	invalidateSelf();
    }

    /**
     * Sets the intrinsic (default) height for this shape.
     * 
     * @param height
     *            the intrinsic height (in pixels)
     */
    public void setIntrinsicHeight(int height) {
	mPieState.mIntrinsicHeight = height;
	invalidateSelf();
    }

    @Override
    public int getIntrinsicWidth() {
	return mPieState.mIntrinsicWidth;
    }

    @Override
    public int getIntrinsicHeight() {
	return mPieState.mIntrinsicHeight;
    }

    private static int modulateAlpha(int paintAlpha, int alpha) {
	int scale = alpha + (alpha >>> 7); // convert to 0..256
	return paintAlpha * scale >>> 8;
    }

    @Override
    public void draw(Canvas canvas) {
	Rect r = getBounds();
	Paint paint = mPieState.mFillPaint;

	int prevAlpha = paint.getAlpha();
	paint.setAlpha(modulateAlpha(prevAlpha, mPieState.mAlpha));

	int count = canvas.save();
	canvas.translate(r.left, r.top);

	final float sweepAngle = 360f / mPieState.mColors.length;
	final float stroke = mPieState.mStrokePaint.getStrokeWidth();
	final Paint p = new Paint(mPieState.mFillPaint);
	for (int i = 0; i < mPieState.mColors.length; i++) {
	    int color = mPieState.mColors[i];
	    p.setColor(color);
	    canvas.drawArc(new RectF(stroke, stroke, getIntrinsicWidth() - stroke, getIntrinsicHeight() - stroke), mPieState.mRotation + sweepAngle * i, sweepAngle, true, p);
	}
	canvas.drawOval(new RectF(stroke / 2, stroke / 2, getIntrinsicWidth() - stroke / 2, getIntrinsicHeight() - stroke / 2), mPieState.mStrokePaint);

	canvas.restoreToCount(count);

	paint.setAlpha(prevAlpha);
    }

    @Override
    public int getChangingConfigurations() {
	return super.getChangingConfigurations() | mPieState.mChangingConfigurations;
    }

    /**
     * Set the alpha level for this drawable [0..255]. Note that this drawable
     * also has a color in its paint, which has an alpha as well. These two
     * values are automatically combined during drawing. Thus if the color's
     * alpha is 75% (i.e. 192) and the drawable's alpha is 50% (i.e. 128), then
     * the combined alpha that will be used during drawing will be 37.5% (i.e.
     * 96).
     */
    @Override
    public void setAlpha(int alpha) {
	mPieState.mAlpha = alpha;
	invalidateSelf();
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
	mPieState.mFillPaint.setColorFilter(cf);
	mPieState.mStrokePaint.setColorFilter(cf);
	mPieState.mDividerPaint.setColorFilter(cf);
	invalidateSelf();
    }

    @Override
    public int getOpacity() {
	return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void setDither(boolean dither) {
	mPieState.mFillPaint.setDither(dither);
	mPieState.mStrokePaint.setDither(dither);
	mPieState.mDividerPaint.setDither(dither);
	invalidateSelf();
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
	super.onBoundsChange(bounds);
	updateDrawable();
    }

    private void updateDrawable() {
	final Rect r = getBounds();
	setIntrinsicWidth(r.width());
	setIntrinsicHeight(r.height());
	invalidateSelf();
    }

    @Override
    public ConstantState getConstantState() {
	mPieState.mChangingConfigurations = getChangingConfigurations();
	return mPieState;
    }

    @Override
    public Drawable mutate() {
	if (!mMutated && super.mutate() == this) {
	    mPieState.mFillPaint = new Paint(mPieState.mFillPaint);
	    mPieState.mStrokePaint = new Paint(mPieState.mStrokePaint);
	    mPieState.mDividerPaint = new Paint(mPieState.mDividerPaint);
	    mPieState.mColors = Arrays.copyOf(mPieState.mColors, mPieState.mColors.length);
	    mMutated = true;
	}
	return this;
    }

    /**
     * Defines the intrinsic properties of this ShapeDrawable's Shape.
     */
    final static class PieState extends ConstantState {
	int mChangingConfigurations;
	Paint mFillPaint;
	Paint mStrokePaint;
	Paint mDividerPaint;
	int[] mColors;
	float mRotation;
	int mIntrinsicWidth;
	int mIntrinsicHeight;
	int mAlpha = 255;

	PieState(PieState orig) {
	    if (orig != null) {
		mFillPaint = orig.mFillPaint;
		mStrokePaint = orig.mStrokePaint;
		mDividerPaint = orig.mDividerPaint;
		mColors = orig.mColors;
		mRotation = orig.mRotation;
		mIntrinsicWidth = orig.mIntrinsicWidth;
		mIntrinsicHeight = orig.mIntrinsicHeight;
		mAlpha = orig.mAlpha;
	    } else {
		mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mFillPaint.setStyle(Style.FILL);
		mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mStrokePaint.setStyle(Style.STROKE);
		mStrokePaint.setStrokeWidth(1);
		mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mDividerPaint.setStyle(Style.STROKE);
		mDividerPaint.setStrokeWidth(1);
	    }
	}

	@Override
	public Drawable newDrawable() {
	    return new PieDrawable(this);
	}

	@Override
	public Drawable newDrawable(Resources res) {
	    return new PieDrawable(this);
	}

	@Override
	public int getChangingConfigurations() {
	    return mChangingConfigurations;
	}
    }

}
